%{
#include <stdio.h>
#include <string.h>
#include "table.h"
#include "coolinary.h"
#include "vegetables.h"
#include "meats.h"
#include "grains.h"
#include "fruits.h"
#define NSIZE 1094
#define TSIZE  512

static table symboltable;
static table nutritiontable;

extern FILE* yyin;


	/* called before the parsing - loads food values into the nutrition table and creates the symbol table */

void coolinary_init(void) {

       nutritiontable = table_init(NSIZE);
	
	addFruits(&nutritiontable);
	addMeats(&nutritiontable);
	addVeggies(&nutritiontable);
	addGrains(&nutritiontable);
	
	symboltable = table_init(TSIZE);
	
}

void yyerror(const char *str) { fprintf(stderr,"error: %s\n",str); }

/*_____________________________________________new structs in coolinary.h_____________________________________________*/

/*_____________________________________________execute______________________________________________________________*/

	/*function that executes all of the statments in a statement list.  Called by program or by a control statement*/

	void execute (struct statement_list stmt_list) {
		int i=0;
		/*all functions are void with 1 parameter - of type struct params_list - so the call is the same*/
		for (i;i<stmt_list.num_statements;i++) {
			stmt_list.s_list[i].fpointer (stmt_list.s_list[i].p_list);
		}
	}

/*_____________________________________________helper functions________________________________________________*/
	
	/*converts a list of ids (strings) to a list of params */
	/*doesn't do any type checking*/

	struct param_list ids_to_params (struct id_list ids)
	{
		
		/*allocate space*/	
		struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));

		/*"convert" from strings to addresses*/
		int i = 0;
		for (i;i<ids.num_ids;i++) {
			new->list[i].value = (long) ids.list[i];
		}

		/*set number*/
		new->num_params = ids.num_ids;

		/*maybe should return a pointer instead?*/
		return *new;
	}

	/*gets an address from a variable in the symbol table.  rh_address is a void pointer addr and an int size.  the second parameter is the typename - 
	this will test for whether the retrieved item is of the same type - and if not give an error*/

	rh_address* get_address_size (char* name, int typename, int typename2)
	{
		lookup_result *tl = table_includes(symboltable, name);

		if (tl->index == -1) {
			yyerror("Compile-Time Error");
			printf("Variable %s is not declared before use.\n", name);
			exit(-1);
		}
		else if (tl->type != typename && tl->type != typename2) {
			yyerror("Compile-Time Error");
			printf("Type mismatch with regards to variable %s.\n", name);
			exit(-1);
		}

		return table_retrieve (symboltable, tl->index);
	}

	/*gets the type for a variable from the symbol table - returned as an int enum
	gives an error if it doesn't exist*/

	int get_type (char* name) {
		lookup_result *tl = table_includes(symboltable, name);

		if (tl->index == -1) {
			yyerror("Compile-Time Error");
			printf("Variable %s is not defined before use.\n", name);
			exit(-1);
		}

		return tl->type;
	}

	/*returns a string representing an integer*/

	char* itoa (int a) 
	{
		int length = 1;
		int i, count;


		{for (i=10;((i-.01) < a);i*=10,length++);}

		char* new = (char*) malloc (length+1);

		for (count = 0, i /= 10; count<length;count++,i/=10) {
			*(new + count) = (a / i) + 48;
			a -= (a / i) * i;
		}

		*(new + length) = '\0';

		return new;
	}

	float power (float a, int b) 
	{
		int i = 0;
		float total = 1;
		for (i;i<b;i++) {
			total *= a;
		}

		return total;
	}
				
		
				

/*_____________________________________________expression functions_______________________________________________*/

	/* evaluates an arithmetric function all od these have two operands - which may be variables or may be accessors - or may be values
	They cannot themselves be arithmetic functions*/
	
	int int_evaluate (struct param_list params, int op) {
		int vals[2];
		int i = 0;
		int v_type;

		/*idea is to reduce the parameter to an int value - then perform the requested operation on the int values - which are stored in the vals array
		if it is a valid string concat operation - it comes here - but we return zero.  
		otherwise if it isn't an integer type this gives an error */

		for (i = 0; i<2; i++) {
			if (params.list[i].is_name) {
				v_type = get_type ((char*) params.list[i].value);

				/*could be be string concat*/
				if (i == 0 && v_type == CSTRING) {return -1;}

				/*error*/
				else if (v_type != INTEGER) {
					yyerror ("Coolinary binary operators are limited to integer types.\n");
					exit (-1);
				}

				rh_address* temp = get_address_size ((char*) params.list[i].value, INTEGER, -1);
				vals[i] = *((int*) temp->addr);
			}
			else if (params.list[i].is_accsr) {
				struct param_list *accsr = (struct param_list*) params.list[i].value;
				
				/*could be string concat*/
				if (i==0 && accsr->list[1].value == FNAME) {
					return -1;
				}
				else {
					rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
					switch (accsr->list[1].value) {
						case FNAME:  yyerror ("name cannot be used as an operand in an integer context"); exit(-1); break;
						case FQUANTITY: vals[i] = ((cooltype *) rh_addr->addr)->quantity; break;
						case FNUTRITION: vals[i] = ((cooltype *) rh_addr->addr)->nutrition; break;
						case FTIME: vals[i] = ((cooltype *) rh_addr->addr)->time; break;			
					}
				}
			}
				
			else {
				/*test for possible string concat*/
				if (i == 0 && params.list[0].type == CSTRING) {return -1;}
				else if (params.list[i].type != INTEGER) {
					yyerror ("Coolinary binary operators are limited to integer types.\n");
					exit (-1);
				}
				vals[i] = params.list[i].value;
			}
		}

		switch (op) {
			case ADD: return (vals[0] + vals[1]); break;
			case SUBTRACT: return (vals[0] - vals[1]); break;
			case MULTIPLY: return (vals[0] * vals[1]); break;
			case DIVIDE: return (vals[0] / vals[1]); break;
			case LTHAN: return (vals[0] < vals[1]); break;
			case LTHANE: return (vals[0] <= vals[1]); break;
			case EQUALS: return (vals[0] == vals[1]); break;
			case NEQUALS: return (vals[0] != vals[1]); break;
			case GTHANE: return (vals[0] >= vals[1]); break;
			case GTHAN: return (vals[0] > vals[1]); break;
		}
	}

	/*this is called when an arithmetric expression only contains literals - the idea is to evaluate it once instead of each time
	in a control setting.  both operands are ints - so you just switch on the operation*/

	int short_int_evaluate (int a, int b, int op) {
		switch (op) {
			case ADD: return (a + b); break;
			case SUBTRACT: return (a - b); break;
			case MULTIPLY: return (a * b); break;
			case DIVIDE: return (a / b); break;
			case LTHAN: return (a < b); break;
			case LTHANE: return (a <= b); break;
			case EQUALS: return (a == b); break;
			case NEQUALS: return (a != b); break;
			case GTHANE: return (a >= b); break;
			case GTHAN: return (a > b); break;
		}
	}

	/*this is the equivalient of the short_int_evaluation for concatenation*/

	char* short_concatanate (char* a, char* b) {
		/*safe way to avoid overwriting anything*/
		int length = strlen(a) + strlen(b) + 2;
		char *new = (char*) malloc (length);

		memcpy (new,a,strlen(a));
		*(new+strlen(a)) = ' ';
		memcpy (new+strlen(a)+1,b,strlen(b));
		*(new+length-1)= '\0';
		return new;
	}

	/*first operand can be a string literal, string accessor, or string variable.  Second can be any of these or in int expression as well
	Adds the two together with a space in the middle and returns a pointer to this.  appraoch is to get the two sting values into vals[2]
	and then do the concat on them*/

	char* concatanate (struct param_list params) {
		char* vals[2];
				
		/* first param can be string literal or string var or name accessor */
		if (params.list[0].is_name) {
			vals[0] = (char*) get_address_size((char*) params.list[0].value, CSTRING, -1)->addr;
		}
		else if (params.list[0].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[0].value;
			if (accsr->list[1].value != FNAME) {yyerror("First operand to string concatenation must be of type string");exit(-1);}
			else {vals[0] = ((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP, -1)->addr)->name;}
		}			
		else {
			vals[0] = (char*) params.list[0].value;
		}

		/*second param can be string literal or int literal or int var or string var or string or int accessor*/
		if (params.list[1].is_name) {
			int type = get_type ((char*) params.list[1].value);
			switch (type) {
				case CSTRING: vals[1] = (char*) get_address_size((char*) params.list[1].value, CSTRING, -1)->addr; break;
				case INTEGER: vals[1] = itoa (*( (long*) get_address_size((char*) params.list[1].value, INTEGER, -1)->addr)); break;
				default:  yyerror ("Invalid type for + operator.\n"); exit(-1); break;
			}
		}
		else if (params.list[1].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[1].value;
			switch (accsr->list[1].value) {
				case FNAME: vals[1] = ((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP, -1)->addr)->name; break;
				case FQUANTITY: vals[1] = itoa(((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP, -1)->addr)->quantity); break;
				case FNUTRITION: vals[1] = itoa(((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP, -1)->addr)->nutrition); break;
				case FTIME: vals[1] = itoa(((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP, -1)->addr)->time); break;				
			}
		}
		else {
			switch (params.list[1].type) {
				case CSTRING: vals[1] = (char*) params.list[1].value; break;
				case INTEGER:	vals[1] = itoa(params.list[1].value); break;
				default: yyerror ("Illegal type in + expression.\n"); exit (-1); break;
			}
		}

		/*concat the strings*/
		return short_concatanate (vals[0], vals[1]);
	}
			


		

/*______________________________________________statement functions____________________________________________*/

	/* function to declare ints.  takes a list of ids - and loops through them.  Will give an error either if the function is already in the symbol table.  
	input is an array of id names*/

	void int_declare (struct param_list params) {
		int i = 0;
		for (i;i<params.num_params;i++) {
			if( table_includes(symboltable, (char*) params.list[i].value)->index == -1) {
				int* temp = (int*) malloc (sizeof (int));
				table_insert(symboltable, (char*) params.list[i].value, INTEGER, (void*) temp, sizeof(int));
				free (temp);
			}
			else {
				yyerror("Compile-Time Error");
				printf("Variable %s already defined.\n", (char*) params.list[i].value);
				exit(-1);
			}
		}
	}

	/*same as ints just for strings*/

	void string_declare (struct param_list params) 
	{
		int i = 0;
		for (i;i<params.num_params;i++) {
			if( table_includes(symboltable, (char*) params.list[i].value)->index == -1) {
				char* temp = (char*) malloc (1);
				*temp = '\0';
				table_insert(symboltable, (char*) params.list[i].value, CSTRING, (void*) temp, 1);
				free (temp);
			}
			else {
				yyerror("Compile-Time Error");
				printf("Variable %s already defined.\n", (char*) params.list[i].value);
				exit(-1);
			}
		}
	}

	/*slightly more involved.  Here we retrieve the food group information from the nutritiontable first.  then we set the name to 
	id, and then add to the symbol table.  the param_list contains the id's - and then the last entry is the name of the food group.*/ 

	void fgroup_declare (struct param_list params)
	{ 
		int i;
		char* name = (char*) params.list[params.num_params-1].value;

		for (i = 0;i<params.num_params - 1;i++) {
			if( table_includes(symboltable, (char*) params.list[i].value)->index == -1) {
				/*get a pointer to the defined type with the right name */
				rh_address *temp = table_retrieve(nutritiontable, table_includes(nutritiontable, name)->index);
				cooltype *c_temp = (cooltype*) temp->addr;
				
				/*this changes the name in the nutritiontable - but I don't really care*/
				c_temp->name = (char*) params.list[i].value;

				table_insert(symboltable, (char*) params.list[i].value, FGROUP, (void*) c_temp, sizeof (cooltype));
			}
			else {
				yyerror("Compile-Time Error");
				printf("Variable %s already defined.\n", (char*) params.list[i].value);
				exit(-1);
			}
		}
	}

	void user_declare (struct param_list params)
	{
		/*nine params - 1st is new var - 2nd is the existing one that its most like*/
		
		if ( table_includes(symboltable, (char*) params.list[0].value)->index == -1) {
			/*this shouldn't be called rh_address but this gets the name of the nutrition type that the new type is most like
			*/

			/*test for whether the requested type exists*/
			if (! strncmp ((char*) params.list[1].value, "water", 5)||
			    ! strncmp ((char*) params.list[1].value, "liquid", 7)||
			    ! strncmp ((char*) params.list[1].value, "dairy", 5)||
			    ! strncmp ((char*) params.list[1].value, "meat", 4)||
			    ! strncmp ((char*) params.list[1].value, "vegetable", 9)||
			    ! strncmp ((char*) params.list[1].value, "fruit", 5)
			) {}
			else {
				yyerror ("you must enter a valid type to create your own food group"); exit(-1);
			}
			
			/*in theory we get cooking parameter values for the new type from this type - but these don't exist yet */

			/*create new struct*/
			cooltype *new = (cooltype*) malloc (sizeof (cooltype));
			
			/*assign values */
			new->name = (char*) params.list[0].value;
			new->nutrition = (float) params.list[2].value;
			new->quantity = 1;
			new->time = 0;
			new->type = (char*)  params.list[1].value;
			new->nutrition_values.calories = (float) params.list[3].value;
			new->nutrition_values.protein = (float) params.list[4].value;
			new->nutrition_values.sugar = (float) params.list[5].value;
			new->nutrition_values.fat = (float) params.list[6].value;
			new->nutrition_values.cholesterol = (float) params.list[7].value;
			new->nutrition_values.vitamin_a = (float) params.list[8].value;
			new->nutrition_values.vitamin_c = (float) params.list[9].value;
		

			/*add the new type to the symbol table*/
			table_insert(symboltable, (char*) params.list[0].value, FGROUP, (void*) new, sizeof (cooltype));

			/*might cause problems*/
			free(new);
			}
		else {
			yyerror("Compile-Time Error");
			printf("Variable %s already defined.\n", (char*) params.list[0].value);
			exit(-1);
		}
	}

	/*same as string or int*/
	void mix_declare (struct param_list params) 
	{
		int i = 0;
		for (i;i<params.num_params;i++) {
			if( table_includes(symboltable, (char*) params.list[i].value)->index == -1) {
				struct mixture *temp = (struct mixture*) malloc (sizeof (struct mixture));
				temp ->name = (char*) params.list[i].value;
				table_insert(symboltable, (char*) params.list[i].value, MIXTYPE, (void*) temp, 1);
				free (temp);
			}
			else {
				yyerror("Compile-Time Error");
				printf("Variable %s already defined.\n", (char*) params.list[i].value);
				exit(-1);
			}
		}
	}


			



















		 

	/*the param_list has two parameters.  The first is the ID name to assign to - and the second is the thing to assign to it
	this could be either a literal, a name, an operation, or an accessor.  what it is can be determined with .is_name, .is_addr and .is_expr fields */

	void assign (struct param_list params)
	{
		/*first checks the type of the left side - and checks that it is a valid location*/
		int lh_type = get_type((char*) params.list[0].value);
		int rh;
		struct expression* expr;
			
		/*get the rh value*/
		/*as of now - if it's an expression - then it can only be an int - can't assign a string concat to a string */
		if (params.list[1].is_expr) {
			switch (lh_type) {
				case INTEGER: expr = (struct expression*) params.list[1].value;
						rh = int_evaluate (expr->p_list, expr->op);

						/*tried to assign a string concat*/
						if (rh == -1) {
							yyerror ("string concatanations cannot be assigned to ints\n");
							exit (-1);
						}

						table_assign (symboltable, (char*) params.list[0].value, &rh, sizeof(int)); break;

				case CSTRING: yyerror ("compile-time error.  only string variables or string literals may be assigned to strings.\n");
						exit (-1);
						break;
			}
		}
		else if (params.list[1].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[1].value;
			switch (accsr->list[1].value) {
				case FNAME:	if (lh_type != CSTRING) {yyerror ("Can't assign a name to an int"); exit(-1);}
						else {
							struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
							table_assign (symboltable, (char*) params.list[0].value, ((cooltype *) rh_addr->addr)->name, strlen (((cooltype *) rh_addr->addr)->name));
						} break;
				case FQUANTITY:   	if (lh_type != INTEGER) {yyerror ("Can't assign quantity to non-int variable"); exit(-1);}
							else {
								struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
								table_assign (symboltable, (char*) params.list[0].value, &((cooltype *) rh_addr->addr)->quantity, sizeof (int));
							} break;	
				case FNUTRITION:	if (lh_type != INTEGER) {yyerror ("Can't assign quantity to non-int variable"); exit(-1);}
							else {
								struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
								table_assign (symboltable, (char*) params.list[0].value, &((cooltype *) rh_addr->addr)->nutrition, sizeof (int));
							} break;
				case FTIME:		if (lh_type != INTEGER) {yyerror ("Can't assign quantity to non-int variable"); exit(-1);}
							else {
								struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
								table_assign (symboltable, (char*) params.list[0].value, &((cooltype *) rh_addr->addr)->time, sizeof (int));
							} break;
			}
		}
		
		/*so this is a variable that has to agree*/
		else if (params.list[1].is_name) {	
			rh_address* rh_addr = get_address_size ((char*) params.list[1].value, lh_type, -1);
			switch (lh_type) {
				case INTEGER:  table_assign (symboltable, (char*) params.list[0].value, rh_addr->addr, rh_addr->size); break;
				case CSTRING:	 table_assign (symboltable, (char*) params.list[0].value, rh_addr->addr, rh_addr->size); break;
			}	
		}
			
		/*a literal - again - types have to agree*/
		else {
			if (lh_type != params.list[1].type) {
				yyerror("Compile-Time Error");
				printf("Type mismatch with regards to variable %s.\n", (char*) params.list[0].value);
				exit(-1);
			}
			else {
				switch (lh_type) {
					case INTEGER:  table_assign (symboltable, (char*) params.list[0].value, &params.list[1].value, sizeof(int)); break;
					case CSTRING:	 table_assign (symboltable, (char*) params.list[0].value, (char*) params.list[1].value, strlen((char*) params.list[1].value)); break;
				}
			}
		}
	}

	/*special case of assignment - this is the only method that can be assigned to - only takes an int and doesn't take an arithmetric expression*/

	void assign_quantity (struct param_list params)
	{
		/*get the lh_address*/
		rh_address *rh_addr = get_address_size ((char*) params.list[0].value, FGROUP, -1);
		int* q_addr = &((cooltype*) rh_addr->addr)->quantity;

		/*get the rh value */
		if (params.list[1].is_name) {
			rh_address* rh_addr = get_address_size ((char*) params.list[1].value, INTEGER, -1);
			*q_addr = *( (int*) rh_addr->addr);
		}
		else if (params.list[1].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[1].value;
			rh_address* rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
			switch (accsr->list[1].value) {
				case FNAME: yyerror("cannot assign a string variable to quantity field"); exit (-1); break;
				case FQUANTITY:  *q_addr = ((cooltype*) rh_addr->addr)->quantity; break;
				case FNUTRITION:  *q_addr = ((cooltype*) rh_addr->addr)->nutrition; break;
				case FTIME: *q_addr = ((cooltype*) rh_addr->addr)->time; break;				
			}

			}
		else {
			*q_addr = params.list[1].value;
		}
	}

	
	/*the first param is the condition - which might be a basic and might be an expression.  The second
	is a pointer to the statement list */

	void simple_if (struct param_list params)
	{
		char truth;

		/*get the value of the condition*/
		if (params.list[0].is_expr) {
			struct expression* temp = (struct expression*) params.list[0].value;
			truth = int_evaluate (temp->p_list, temp->op);
		}
		else {
			truth = params.list[0].value;
		}

		/*error if string expression*/
		if (truth == -1) {
			yyerror ("string expressions cannot be used conditionals\n");
			exit (-1);
		}

		/*call execute on the list of statements */
		if (truth) {
			struct statement_list* temp = (struct statement_list*) params.list[1].value;
			execute (*temp);
		}
	}
	
	/*third parameter in list is the else statements list - otherwise the same as above*/	
	void if_then_else (struct param_list params) {
		char truth;

		/*get the value of the condition*/
		if (params.list[0].is_expr) {
			struct expression* temp = (struct expression*) params.list[0].value;
			truth = int_evaluate (temp->p_list, temp->op);
		}

		else {
			truth = params.list[0].value;
		}

		/*error if string expression*/
		if (truth == -1) {
			yyerror ("string expressions cannot be used as conditionals\n");
			exit (-1);
		}

		/*call execute on the list of statements */
		if (truth) {
			struct statement_list* temp = (struct statement_list*) params.list[1].value;
			execute (*temp);
		}
		/*or on the other list of statements*/
		else {
			struct statement_list* temp = (struct statement_list*) params.list[2].value;
			execute (*temp);
		}
	}

	/*same as the simple_if except that it's inside a loop and retests every time */
	void while_loop (struct param_list params) {
		char truth = 1;

		while (truth) {
		
			/*get the value of the condition*/
			if (params.list[0].is_expr) {
				struct expression* temp = (struct expression*) params.list[0].value;
				truth = int_evaluate (temp->p_list, temp->op);
			}

			else {
				truth = params.list[0].value;
			}

			/*error if string expression*/
			if (truth == -1) {
				yyerror ("string expressions cannot be used as conditionals\n");
				exit (-1);
			}


			/*call execute on the list of statements */
			if (truth) {
				struct statement_list* temp = (struct statement_list*) params.list[1].value;
				execute (*temp);
			}
		}
	}

	/*first param could be value or address - second param is the execution list -  form of a loop that does something a set number of times - doesn't reevaluate*/
	void repeat_loop (struct param_list params) {
		int num_times;
		int i=0;

		/*this inherently tests that it is an int*/
		if (params.list[0].is_name) {
			struct rh_address *rh_addr = get_address_size ((char*) params.list[0].value, INTEGER, -1);
			num_times = *((int*) rh_addr->addr);	
		}
		else if (params.list[0].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[0].value;
			rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
			switch (accsr->list[1].value) {
				case FNAME:	yyerror ("repeat statement takes int, not string"); exit(-1);break;
				case FQUANTITY:	num_times = ((cooltype *) rh_addr->addr)->quantity; break;
				case FNUTRITION:	num_times = ((cooltype *) rh_addr->addr)->nutrition; break;
				case FTIME:		num_times = ((cooltype *) rh_addr->addr)->time; break;
			}
		}			
		else {
			num_times = params.list[0].value;
		}

		for (i;i<num_times;i++) {
			struct statement_list* temp = (struct statement_list*) params.list[1].value;
			execute (*temp);
		}
	}

	/*prints a string, string variable, string accessor, or a print concat expression*/

	void print (struct param_list params) {
		if (params.list[0].is_expr) {
			struct expression *expr = (struct expression*) params.list[0].value;
			int ev = int_evaluate (expr->p_list, expr->op);

			/*wasn't string concat*/
			if (ev != -1) {
				yyerror ("print only takes string or string expressions.\n");
				exit (-1);
			}
			/*might be string concat - second type still need checked*/
			else {
				char *to_print = concatanate (expr->p_list);
				printf ("%s\n", to_print);
			}
		}
		else if (params.list[0].is_accsr) {
			struct param_list *tlist = (struct param_list*) params.list[0].value;
			
			if (tlist->list[1].value != FNAME) {yyerror ("accessors other than name may not be printed in this fashion"); exit(-1);}

			struct rh_address *rh_addr = get_address_size ((char*) tlist->list[0].value, FGROUP, -1);
			printf ("%s\n", ((cooltype*) rh_addr->addr)->name);
		}	
		else if (params.list[0].is_name) {
			struct rh_address *rh_addr = get_address_size ((char*) params.list[0].value, CSTRING, -1);
			printf ("%s\n", (char*) rh_addr->addr);
			}
		else {
			printf ("%s\n", (char*) params.list[0].value);
		}
	}

	/*cooking functions*/
	int bake (cooltype* object, int minutes)
	{
		char* f_type = object->type;

		if (!strncmp (f_type,"water",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"liquid",6)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"dairy",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"fruit",5)) {
			int threshold = 15;
			float b_factor = 1.025;
			float a_factor = .95;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}
		else if (!strncmp (f_type,"grain",5)) {
			int threshold = 40;
			float b_factor = 1.125;
			float a_factor = .975;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}
		else if (!strncmp (f_type,"vegetable",9)) {
			int threshold = 40;
			float b_factor = 1.015;
			float a_factor = .95;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}
		else if (!strncmp (f_type,"meat",4)) {
			int threshold = 50;
			float b_factor = 1.15;
			float a_factor = .95;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}

		return 0;
	}

	int boil (cooltype* object, int minutes) 
	{
		char* f_type = object->type;

		if (!strncmp (f_type,"water",5)) {
			int threshold = 3;
			float b_factor = 1.005;
			float a_factor = .99;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}
		else if (!strncmp (f_type,"liquid",6)) {
			int threshold = 5;
			float b_factor = 1.005;
			float a_factor = .99;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}
		else if (!strncmp (f_type,"dairy",5)) {
			int threshold = 5;
			float b_factor = 1;
			float a_factor = .985;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}
		else if (!strncmp (f_type,"fruit",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"grain",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"vegetable",9)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"meat",4)) {
			return object->quantity;
		}

		return 0;
	}

	int grill (cooltype* object, int minutes) 
	{
		char* f_type = object->type;

		if (!strncmp (f_type,"water",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"liquid",6)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"dairy",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"fruit",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"grain",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"vegetable",9)) {
			int threshold = 5;
			float b_factor = 1.005;
			float a_factor = .96;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}
		else if (!strncmp (f_type,"meat",4)) {
			int threshold = 10;
			float b_factor = 1.04;
			float a_factor = .975;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
		}

		return 0;
	}	

	int fry (cooltype* object, int minutes) 
	{
		char* f_type = object->type;

		if (!strncmp (f_type,"water",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"liquid",6)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"dairy",5)) {
			return object->quantity;
		}
		else if (!strncmp (f_type,"fruit",5)) {
			int threshold = 4;
			float b_factor = 1.00;
			float a_factor = .95;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
			object->nutrition_values.fat++;
		}
		else if (!strncmp (f_type,"grain",5)) {
			int threshold = 10;
			float b_factor = 1.00;
			float a_factor = .975;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
			object->nutrition_values.fat++;		
		}
		else if (!strncmp (f_type,"vegetable",9)) {
			int threshold = 10;
			float b_factor = 1.01;
			float a_factor = .975;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
			object->nutrition_values.fat++;
		}
		else if (!strncmp (f_type,"meat",4)) {
			int threshold = 10;
			float b_factor = 1.00;
			float a_factor = .95;

			int total_time = minutes + object->time;
			int before = (total_time > threshold) ? threshold : total_time;
			int after = (total_time > threshold) ? total_time - threshold : 0;

			float temp = object->nutrition;
			temp *= power (b_factor, before);
			temp *= power (a_factor, after);
			object->nutrition = (int) temp;

			object->time += minutes;
			object->nutrition_values.fat+=1.5;
		}

		return 0;
	}

	void mix_bake (struct mixture* object, int minutes) 
	{
		int i = 0, total=0;
		float bad = 0;
		for (i; i < object->num_items;i++) {
			bad += bake(object->list[i], minutes);
			total += object->list[i]->quantity;
		}

		float ratio = bad / total;

		if (ratio > .33) {
			yyerror ("bake is not valid on this operation"); exit (-1);
		}
	}

	void mix_boil (struct mixture* object, int minutes) 
	{
		int i = 0, total=0;
		float bad = 0;
		for (i; i < object->num_items;i++) {
			bad += bake (object->list[i], minutes);
			total += object->list[i]->quantity;
		}

		float ratio = 1 - (bad / total);

		printf ("%f %d %f", bad, total, ratio);

		if (ratio > .33) {
			yyerror ("boil is not valid on this operation"); exit (-1);
		}

	}

	void mix_grill (struct mixture* object, int minutes) 
	{
		int i = 0, total=0;
		float bad = 0;
		for (i; i < object->num_items;i++) {
			bad += bake (object->list[i], minutes);
			total += object->list[i]->quantity;
		}

		float ratio = bad / total;

		if (ratio > .33) {
			yyerror ("grill is not valid on this operation"); exit (-1);
		}
	}

	void mix_fry (struct mixture* object, int minutes) 
	{
		int i = 0, total=0;
		float bad = 0;
		for (i; i < object->num_items;i++) {
			bad += bake (object->list[i], minutes);
			total += object->list[i]->quantity;
		}

		float ratio = bad / total;

		if (ratio > .33) {
			yyerror ("fry is not valid on this operation"); exit (-1);
		}
	}
	

	/*we want a control function so that its easier to deal with which of the different functions work on each variables
	- basically thought this tests that the id that calls the function is of FGROUP type and then calls the function with that address
	and any other parameters*/

	void method (struct param_list params)
	{
		/*get_address of food type*/
		rh_address *lh_addr = get_address_size ((char*) params.list[0].value, FGROUP, MIXTYPE);
		int lh_type = get_type ((char*) params.list[0].value);
		
		/*evaluate the number*/
		int num_times = 0;
		if (params.list[2].is_name) {
			struct rh_address *rh_addr = get_address_size ((char*) params.list[2].value, INTEGER, -1);
			num_times = *((int*) rh_addr->addr);	
		}
		else if (params.list[2].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[2].value;
			rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP, -1);
			switch (accsr->list[1].value) {
				case FNAME:	yyerror ("Cooking functions don't take strings as parameters"); exit(-1);break;
				case FQUANTITY:	num_times = ((cooltype *) rh_addr->addr)->quantity; break;
				case FNUTRITION:	num_times = ((cooltype *) rh_addr->addr)->nutrition; break;
				case FTIME:		num_times = ((cooltype *) rh_addr->addr)->time; break;
			}
		}			
		else {
			num_times = params.list[2].value;
		}

		if (lh_type == FGROUP) {
			int bad = 0;
			/*redirect to appropriate function - one set for mixtures and one set for non-mixtures*/
			if (! strncmp((char*) params.list[1].value, "boil", 4)) {
				bad = boil ( ((cooltype*) lh_addr->addr), num_times);
				if (bad) {
					printf ("$s", (char*) ((cooltype*) lh_addr->addr)->name);  yyerror (" can't be boiled"); exit (-1);
				}
			}
			else if (! strncmp((char*) params.list[1].value, "bake", 4)) {
				bad = bake ( ((cooltype*) lh_addr->addr), num_times);
				if (bad) {
					printf ("$s", (char*) ((cooltype*) lh_addr->addr)->name);  yyerror (" can't be baked"); exit (-1);
				}
			}
			else if (! strncmp((char*) params.list[1].value, "grill", 5)) {
				bad = grill ( ((cooltype*) lh_addr->addr), num_times);
				if (bad) {
					printf ("$s", (char*) ((cooltype*) lh_addr->addr)->name);  yyerror (" can't be grilled"); exit (-1);
				}
			}
			else if (! strncmp((char*) params.list[1].value, "fry", 3)) {
				bad = fry ( ((cooltype*) lh_addr->addr), num_times);
				if (bad) {
					printf ("$s", (char*) ((cooltype*) lh_addr->addr)->name);  yyerror (" can't be fried"); exit (-1);
				}
			}
		}
		else {/* is MIX */
			if (! strncmp((char*) params.list[1].value, "boil", 4)) {
				mix_boil ( ((struct mixture*) lh_addr->addr), num_times);
			}
			else if (! strncmp((char*) params.list[1].value, "bake", 4)) {
				mix_bake ( ((struct mixture*) lh_addr->addr), num_times);
			}
			else if (! strncmp((char*) params.list[1].value, "grill", 5)) {
				mix_grill ( ((struct mixture*) lh_addr->addr), num_times);
			}
			else if (! strncmp((char*) params.list[1].value, "fry", 3)) {
				mix_fry ( ((struct mixture*) lh_addr->addr), num_times);
			}
		}
	}

	void combine (struct param_list params)
	{
		/*take the name of the lh_side of the top */
		/*create a new mixture type*/
		struct mixture *new = (struct mixture*) get_address_size ((char*) params.list[params.num_params].value, MIXTYPE, -1)->addr;
		
		new->name = (char*) params.list[params.num_params].value;
	
		int i;
		int j;
		int num_added = 0;
		struct mixture *temp;
		for (i = 0; i<params.num_params; i++) {

			
			int lh_type = get_type ((char*) params.list[i].value);
			cooltype* dummy = (cooltype*) malloc (sizeof (cooltype));
			
			switch (lh_type) {
				case FGROUP:  *dummy = *((cooltype*) get_address_size((char*) params.list[i].value, FGROUP, -1)->addr);
						new->list[num_added] = dummy; num_added++; break;


				case MIXTYPE:  temp = (struct mixture*) get_address_size((char*) params.list[i].value, MIXTYPE, -1)->addr;
						 for (j=0;j<temp->num_items;j++) {
							*dummy = *((cooltype*) get_address_size((char*) temp->list[j]->name, FGROUP, -1)->addr);
							new->list[num_added] = dummy;
							num_added++;
						 } break;
				default: yyerror ("only mixtures or food items may be combined\n"); exit(-1); break;
			}
		}

		/*determine if we need to change the types of the object in the array*/

		/*set num_items*/
		new->num_items = num_added;
	}

	void display (struct param_list params)
	{
		/*test that the variable is a mixture and get a temp */
		struct mixture *temp = (struct mixture*) get_address_size((char*) params.list[0].value, MIXTYPE, -1)->addr;

		/*get temporary cooltype and then an array of names and of the highest scorers for each type*/
		cooltype* averaged = (cooltype*) malloc (sizeof (cooltype));
		char* bests[7];
			int i;
			for(i=1;i<7;i++) {
				bests[i] = (char*) malloc (50);
				bests[i] = "\0";
			}
		float best_vals[7];
			for (i=1;i<7;i++) {
				best_vals[i] = 0;
			}

		char* all[20];
		int ing = 0;

		/*go through each and get values and such*/
		for (i = 0;i<temp->num_items;i++) {
			cooltype* current = temp->list[i];

			all[i] = current->name;
			ing++;

			averaged->quantity += current->quantity;
			averaged->nutrition += current->nutrition;

			averaged->nutrition_values.calories += current->nutrition_values.calories;
			if (current->quantity * current->nutrition_values.calories > best_vals[0]) {
				best_vals[0] = current->quantity * current->nutrition_values.calories;
				bests[0] = current->name;
			}

			averaged->nutrition_values.fat += current->nutrition_values.fat;
			if (current->quantity * current->nutrition_values.fat > best_vals[1]) {
				best_vals[1] = current->quantity * current->nutrition_values.fat;
				bests[1] = current->name;
			}

			averaged->nutrition_values.cholesterol += current->nutrition_values.cholesterol;
			if (current->quantity * current->nutrition_values.cholesterol > best_vals[2]) {
				best_vals[2] = current->quantity * current->nutrition_values.cholesterol;
				bests[2] = current->name;
			}

			averaged->nutrition_values.sugar += current->nutrition_values.sugar;
			if (current->quantity * current->nutrition_values.sugar > best_vals[3]) {
				best_vals[3] = current->quantity * current->nutrition_values.sugar;
				bests[3] = current->name;
			}

			averaged->nutrition_values.protein += current->nutrition_values.protein;
			if (current->quantity * current->nutrition_values.protein > best_vals[4]) {
				best_vals[4] = current->quantity * current->nutrition_values.protein;
				bests[4] = current->name;
			}

			averaged->nutrition_values.vitamin_a += current->nutrition_values.vitamin_a;
			if (current->quantity * current->nutrition_values.vitamin_a > best_vals[5]) {
				best_vals[5] = current->quantity * current->nutrition_values.vitamin_a;
				bests[5] = current->name;
			}

			averaged->nutrition_values.vitamin_c += current->nutrition_values.vitamin_c;
			if (current->quantity * current->nutrition_values.vitamin_c > best_vals[6]) {
				best_vals[6] = current->quantity * current->nutrition_values.vitamin_c;
				bests[6] = current->name;
			}			
		}
	
		/*do the displaying */
		printf ("\t\t\t\tNUTRITIONAL INFORMATION FOR %s\n", temp->name);
		printf ("Ingredients: ");
			for (i=0;i<ing;i++) {
				printf ("%s ", all[i]);
			} printf ("\n");
		printf ("\t\tNUMBER OF SERVINGS:\t%d\n", averaged->quantity);
		printf ("\t\tNUTRITION SCORE:\t\%d\n", averaged->nutrition / averaged->quantity);
		printf ("\t\tCALORIES:\t\t%f", averaged->nutrition_values.calories / averaged->quantity);
		printf ("\tBIGGEST CONTRIBUTOR: %s\n", bests[0]);
		printf ("\t\tFAT:\t\t\t%f", averaged->nutrition_values.fat / averaged->quantity);
		printf ("\tBIGGEST CONTRIBUTOR: %s\n", bests[1]);
		printf ("\t\tCHOLESTEROL:\t\t%f", averaged->nutrition_values.cholesterol / averaged->quantity);
		printf ("\tBIGGEST CONTRIBUTOR: %s\n", bests[2]);
		printf ("\t\tSUGAR:\t\t\t%f", averaged->nutrition_values.sugar / averaged->quantity);
		printf ("\tBIGGEST CONTRIBUTOR: %s\n", bests[3]);
		printf ("\t\tPROTEIN:\t\t%f", averaged->nutrition_values.protein / averaged->quantity);
		printf ("\tBIGGEST CONTRIBUTOR: %s\n", bests[4]);
		printf ("\t\tVITAMIN A:\t\t%f", averaged->nutrition_values.vitamin_a / averaged->quantity);
		printf ("\tBIGGEST CONTRIBUTOR: %s\n", bests[5]);
		printf ("\t\tVITAMIN C:\t\t%f", averaged->nutrition_values.vitamin_c / averaged->quantity);
		printf ("\tBIGGEST CONTRIBUTOR: %s\n", bests[6]);
	}

int yywrap() { 
#ifdef DEBUG
	printf(" < ==== S y m b o l   T a b l e ==== > \n");
	table_print(symboltable);
#endif
return 1; 
} 

int main(int argc, char *argv[]) {
  if( argc != 2 ) {
	printf("Usage: coolinary [filename]\n");
	exit(-1);
  }

  coolinary_init();
  yyin = fopen(argv[1], "r");
  if( yyin == NULL ) {
	printf("coolinary: no input files.\n");
	exit(-2);
  }
  yyparse();
  fclose(yyin);
  return(0);
}

%}
%token OBRACE EBRACE OPAREN EPAREN SEMI COMMA
%token GROUP PRINT INT STRING IF THEN ELSE WHILE REPEAT NAME QUANTITY NUTRITION TIME MIXTURE DISPLAY
%token EQ NE LT LE GT GE PLUS MINUS INC DEC ASSIGN DOT OVER TIMES COMBINE
%token FTYPE MNAME ID ILIT SLIT 

%union {
	long num;
	char* text;
	struct param	param;
	struct param_list plist;
	struct id_list idlist;
	struct statement stmt;
	struct statement_list slist;
};

%type <num> ILIT arith_op rel_op
%type <text> FTYPE MNAME SLIT ID
%type <param> base_type arith_expr relational_expr string_accessor int_accessor
%type <plist> postfix_expr
%type <idlist> decl_list
%type <stmt> stmt decl_stmt assn_stmt expr_stmt selection_stmt iteration_stmt print_stmt method_stmt display_stmt
%type <slist> stmt_list compound_stmt

%%
/*A param is the most basic type.  The value field is a long it.  There are three flags - is_name, is_expr, and is_accsr, which means that the value represents
a pointer to a char, a pointer to an expression, and a pointer to an accessor respectively.  Statements are of type statement - which includes a function pointer and then a list
of params.  As a result - all of the expressions or basic values are param or param_lists - and the statements either convert these to a param_list or assign the param_list.  This 
set-up is so that control functions work - and because of the bottom-up nature of yacc - we need to store a proper ordering of the statements.  This is done by the statement_list structure.
Statement lists are then executed (by calling the execute function) by control functions or by the program production.  */

program: /* empty */
	| compound_stmt		{execute($1);}
	;

compound_stmt:	OBRACE stmt_list EBRACE	{$$ = $2;}
	

stmt_list:	stmt				{$$.num_statements = 1; $$.s_list[0] = $1;}
		| stmt_list stmt	 	{$$.num_statements = $1.num_statements + 1; $$.s_list[$1.num_statements] = $2;}
       	;

stmt:		decl_stmt			{$$ = $1;}
		| assn_stmt			{$$ = $1;}
		| expr_stmt			{$$ = $1;}
		| selection_stmt		{$$ = $1;}
		| iteration_stmt		{$$ = $1;}
		| print_stmt			{$$ = $1;}
		| method_stmt			{$$ = $1;}
		| display_stmt		{$$ = $1;}
       	;

decl_stmt:	INT decl_list	SEMI		{$$.fpointer = int_declare; $$.p_list = ids_to_params ($2);}
	    	| STRING decl_list SEMI	{$$.fpointer = string_declare; $$.p_list = ids_to_params ($2);}
	    	| FTYPE decl_list SEMI	{$$.fpointer = fgroup_declare; $$.p_list = ids_to_params ($2);
						$$.p_list.list[$$.p_list.num_params].value = (long) $1; $$.p_list.num_params++;}
	    	| GROUP ID OPAREN SLIT COMMA ILIT COMMA ILIT COMMA ILIT COMMA ILIT COMMA ILIT COMMA ILIT COMMA ILIT COMMA ILIT EPAREN SEMI /*user-defined group*/
			/*the first param is the name of the id - the second is the Slit - and the rest are the ints.  the format is irritating - 
			but we feel that this functionality is somewhat outside of the base paradigm of the langauge - so much like a casting statement in 
			C++ - the difficulty isn't necessarily a bad thing*/
						{$$.fpointer = user_declare; 
						$$.p_list.list[0].value = (long) $2;  
						$$.p_list.list[1].value = (long) $4;  
						$$.p_list.list[2].value = $6;
						$$.p_list.list[3].value = $8;
						$$.p_list.list[4].value = $10;
						$$.p_list.list[5].value = $12;
						$$.p_list.list[6].value = $14;
						$$.p_list.list[7].value = $16;
						$$.p_list.list[8].value = $18;
						$$.p_list.list[9].value = $20;}
		| MIXTURE decl_list SEMI	{$$.fpointer = mix_declare; $$.p_list = ids_to_params ($2);}			
		;

decl_list:	ID				{$$.num_ids = 1; $$.list[0] = strdup($1);}
		| decl_list COMMA ID 	{$$.num_ids = $1.num_ids + 1; $$.list[$1.num_ids] = strdup($3);}
		;

assn_stmt:	ID ASSIGN base_type SEMI	{$$.fpointer = assign; $$.p_list.list[0].value = (long) $1; $$.p_list.list[1] = $3;}
		| ID ASSIGN arith_expr SEMI		{$$.fpointer = assign; $$.p_list.list[0].value = (long) $1; $$.p_list.list[1] = $3;}
		| ID DOT QUANTITY ASSIGN base_type SEMI	{if (! $5.is_name && ! $5.is_accsr && $5.type != INTEGER) {
									yyerror ("Only integer values can be assigned to a quantity field"); exit(-1);
								}
								$$.fpointer = assign_quantity; $$.p_list.list[0].value = (long) $1; $$.p_list.list[1] = $5;}
		| ID ASSIGN COMBINE OPAREN decl_list EPAREN SEMI {$$.fpointer = combine;  $$.p_list = ids_to_params ($5);
									 $$.p_list.list[$$.p_list.num_params].value = (long) $1;} 
		;


expr_stmt:	postfix_expr SEMI			{$$.fpointer = assign; $$.p_list = $1;}
		;

selection_stmt:	IF OPAREN relational_expr EPAREN compound_stmt					{$$.fpointer = simple_if; $$.p_list.list[0] = $3;
														struct statement_list* new = (struct statement_list*) 
															malloc (sizeof (struct statement_list));
														*new = $5;
														$$.p_list.list[1].value = (long) new;}

			| IF OPAREN relational_expr EPAREN compound_stmt ELSE compound_stmt		{$$.fpointer = if_then_else; $$.p_list.list[0] = $3;
														struct statement_list* new = (struct statement_list*) 
															malloc (sizeof (struct statement_list));
														*new = $5;
														$$.p_list.list[1].value = (long) new;
														struct statement_list* new1 = (struct statement_list*) 
															malloc (sizeof (struct statement_list));
														*new1 = $7;
														$$.p_list.list[2].value = (long) new1;
														}
			;

/*type checking gets done in the repeat_loop function for loops (for the ints) and in the evaluate function for relational_expr's*/
iteration_stmt:	WHILE OPAREN relational_expr EPAREN compound_stmt	{$$.fpointer = while_loop; $$.p_list.list[0] = $3;
											struct statement_list* new = (struct statement_list*) malloc (sizeof (struct statement_list));
											*new = $5;
											$$.p_list.list[1].value = (long) new;											
											} 	

			| REPEAT OPAREN base_type EPAREN compound_stmt		{if ($3.type != INTEGER && $3.type != EMPTY) {
												yyerror ("Repeat only takes integer literals or variables\n");
												exit (-1);
											}
											else {
												$$.fpointer = repeat_loop; $$.p_list.list[0] = $3;
												struct statement_list* new = (struct statement_list*) malloc (sizeof (struct statement_list));
												*new = $5;
												$$.p_list.list[1].value = (long) new;
											}}	
			;

/*second case is only for string concat - this type checking is done in the print function */
print_stmt:	PRINT OPAREN base_type EPAREN SEMI		{if ($3.type != CSTRING && ! $3.is_name && ! $3.is_accsr) {
									yyerror ("print only takes strings or string expressions\n");
									exit (-1);
								}
								else {
									$$.fpointer = print;
									$$.p_list.list[0] = $3;
								}}
		| PRINT OPAREN arith_expr EPAREN SEMI	{if ($3.type != CSTRING && (! $3.is_name && ! $3.is_expr && ! $3.is_accsr)) {
									yyerror ("print only takes strings or string expressions\n");
									exit (-1);
								}
								else {
									$$.fpointer = print;
									$$.p_list.list[0] = $3;
								}}		
		;

method_stmt:	ID DOT MNAME OPAREN base_type EPAREN SEMI	{if ($5.type == CSTRING) {yyerror ("Cooking functions do not take a string as a parameter"); exit (-1);}
									else {$$.fpointer = method;  $$.p_list.list[0].value = (long) $1; $$.p_list.list[1].value = (long) $3;
									$$.p_list.list[2] = $5;}}
		;

display_stmt:	DISPLAY OPAREN ID EPAREN SEMI		{$$.fpointer = display; $$.p_list.list[0].value = (long) $3;}
		;


postfix_expr:	ID INC					{$$.list[0].value = (long) $1; $$.list[0].is_name = 1;
							struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
							temp->op = ADD; 
							temp->p_list.list[0] = $$.list[0];
							temp->p_list.list[1].value = 1;  temp->p_list.list[1].is_name = 0; temp->p_list.list[1].type = INTEGER; 
							$$.list[1].value = (long) temp; $$.list[1].is_name = 0; $$.list[1].is_expr = 1;
							}	
							
		|ID DEC				{$$.list[0].value = (long) $1; $$.list[0].is_name = 1;
							struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
							temp->op = SUBTRACT; 
							temp->p_list.list[0] = $$.list[0];
							temp->p_list.list[1].value = 1;  temp->p_list.list[1].is_name = 0; temp->p_list.list[1].type = INTEGER; 
							$$.list[1].value = (long) temp; $$.list[1].is_name = 0; $$.list[1].is_expr = 1;
							}
		;

/*Type check during the evaluation process*/
arith_expr:	base_type arith_op base_type	{	/*start by testing for whether they are variables - where possible evaluate to a constant so that we won't have to
								recalculate in a looping situation*/
								if ( ((!$1.is_name) && (!$1.is_accsr)) && ((!$3.is_name) && (!$3.is_accsr))) {
									if ($1.type == INTEGER && $3.type == INTEGER) {
										$$.value = short_int_evaluate($1.value, $3.value, $2); $$.is_expr = 0; $$.is_name = 0;
										$$.is_accsr = 0; $$.type = INTEGER;
							 		}
									else if ($1.type == CSTRING && $3.type == CSTRING) {
										$$.value = (long) short_concatanate((char*) $1.value, (char*) $3.value); $$.is_expr = 0; $$.is_name = 0; 
										$$.is_accsr = 0; $$.type = CSTRING;
									}
									else if ($1.type == CSTRING && $3.type == INTEGER) {
										$$.value = (long) short_concatanate((char*) $1.value, itoa($3.value)); $$.is_expr = 0; $$.is_name = 0; 
										$$.is_accsr = 0; $$.type = CSTRING;
									}
									else {
										yyerror ("Coolinary does not allow operations on non-integer or string types.\n");
										exit(-1);
									}
								}
								/*turn int type expression - we don't type check here*/
								else {
									struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
									temp->op = $2;
									temp->p_list.list[0] = $1; temp->p_list.list[1] = $3; 
									$$.value = (long) temp; $$.is_name = 0; $$.is_expr = 1; $$.is_accsr = 0; $$.type = EMPTY;									
								}
							}
		;

/*we're don't type check here - evaluate does this*/
relational_expr:	base_type rel_op base_type 		{if ((! $1.is_name && ! $1.is_accsr) && (! $3.is_name && ! $3.is_accsr)) {
									if ($1.type == INTEGER && $3.type == INTEGER) {
										$$.value = short_int_evaluate($1.value, $3.value, $2); $$.is_expr = 0;
										$$.is_accsr = 0; $$.type = EMPTY;
									} 
							 	}
								else {
									struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
									temp->op = $2; 
									temp->p_list.list[0] = $1; temp->p_list.list[1] = $3; 
									$$.value = (long) temp; $$.is_name = 0; $$.is_expr = 1; $$.is_accsr = 0; $$.type = EMPTY;
								}}
			;

/*rename primitive type or something */

		/*ID's have type empty because we need to check the symbol table to get their type - can't do it in the SDD */
base_type:	ID 				{$$.value = (long) $1; $$.is_name = 1; $$.is_expr = 0; $$.is_accsr = 0; $$.type = EMPTY;}
		| ILIT				{$$.value = $1; $$.is_name = 0; $$.is_expr = 0; $$.is_accsr = 0; $$.type = INTEGER;}
		| SLIT 			{$$.value = (long) $1; $$.is_name = 0; $$.is_expr = 0; $$.is_accsr = 0; $$.type = CSTRING;}
		| string_accessor 		{$$ = $1;}
		| int_accessor		{$$ = $1;}
		;

string_accessor:	ID DOT NAME 		{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (long) $1; new->list[1].value = FNAME; $$.value = (long) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			;

int_accessor:		ID DOT QUANTITY 	{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (long) $1; new->list[1].value = FQUANTITY; $$.value = (long) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			|ID DOT NUTRITION 	{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (long) $1; new->list[1].value = FNUTRITION; $$.value = (long) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			|ID DOT TIME		{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (long) $1; new->list[1].value = FTIME; $$.value = (long) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			;

arith_op:	PLUS			{$$ = ADD;}
		| MINUS		{$$ = SUBTRACT;}
		| TIMES		{$$ = MULTIPLY;}
		| OVER			{$$ = DIVIDE;}
		;

rel_op:	LT			{$$ = LTHAN;}
		|LE			{$$ = LTHANE;}
		|EQ			{$$ = EQUALS;}
		|NE			{$$ = NEQUALS;}
		|GE			{$$ = GTHANE;}
		|GT			{$$ = GTHAN;}
		;
